﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using YakShaver;
using YakShaver.Contract;
using YakShaver.Tests.DummyObjects;
using System.Threading;
using System.Workflow;
using System.Workflow.Activities;
using System.Workflow.Runtime;
using System.Workflow.Runtime.Tracking;

namespace YakShaver.Tests.Workflow
{
    /// <summary>
    /// Summary description for WorkflowIntegrationTests
    /// </summary>
    [TestClass]
    public class WorkflowIntegrationTests : IntegrationTestBase
    {
        public WorkflowIntegrationTests()
        {
            //
            // TODO: Add constructor logic here
            //
        }

        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        #region Additional test attributes
        //
        // You can use the following additional attributes as you write your tests:
        //
        // Use ClassInitialize to run code before running the first test in the class
        [ClassInitialize()]
        public static void ClassInitialize(TestContext testContext) {
            IntegrationTestBase.MyClassInitialize(testContext);
        }
        
        // Use ClassCleanup to run code after all tests in a class have run
        [ClassCleanup()]
        public static void ClassCleanup() {
            IntegrationTestBase.MyClassCleanup();
        }
        //
        // Use TestInitialize to run code before running each test 
        [TestInitialize()]
        public void MyTestInitialize() 
        {
            DataExchangeService = new ExternalDataExchangeService();
            WorkflowEngine.AddService(DataExchangeService);
        }
        //
        // Use TestCleanup to run code after each test has run
        [TestCleanup()]
        public void MyTestCleanup() { 
        // tear down any services in case an assert failed or other exception was thrown
            ExternalDataExchangeService service = DataExchangeService;
            UnitTestHelper.TestCleanup(ref service);
            WorkflowEngine.RemoveService(
                WorkflowEngine.GetService<ExternalDataExchangeService>());
            DataExchangeService = null;
        }
        
        #endregion
        [TestMethod, TestProperty("Category", @"Workflow\Integration\NotificationServiceTests")]
        public void TestNotifyForIntervention()
        {
            INotificationDummyService notificationService = new INotificationDummyService();
            IAnalysisDummyService analysisService = new IAnalysisDummyService();
            AutoResetEvent wfIdledEvent = new AutoResetEvent(false);
            Dictionary<string, object> namedParameters = new Dictionary<string, object>();
            IWorkItemDataContract dataContract = new WorkItemDataContractDummyObject();

            DataExchangeService.AddService(notificationService);
            DataExchangeService.AddService(analysisService);

            namedParameters.Add("WorkItemDataContract", dataContract);
            
            WorkflowInstance workflowInstance = WorkflowEngine.CreateWorkflow(typeof(WorkItemEntryWorkflow), namedParameters);

            // attach events if I want.

            WorkflowEngine.WorkflowPersisted += delegate (object sender, WorkflowEventArgs args) 
            {
                if (args.WorkflowInstance.InstanceId == workflowInstance.InstanceId)
                    wfIdledEvent.Set();
            };

            workflowInstance.Start();

            wfIdledEvent.WaitOne(new TimeSpan(0, 0, 60), true);
            //grab the latest from the persistence store.  If it doesn't find it, an exception will be thrown
            workflowInstance = WorkflowEngine.GetWorkflow(workflowInstance.InstanceId);

            Assert.IsTrue(notificationService.NotifyForInterventionMethodCallCount > 0, "Notification Service did not return that it sent notification");
        }



        [TestMethod, TestProperty("Category", @"Workflow\Integration\NotificationServiceTests")]
        public void TestHandleInterventionNeeded()
        {

            INotificationDummyService notificationService = new INotificationDummyService();
            IAnalysisDummyService analysisService = new IAnalysisDummyService();
            IWorkItemProviderService providerService = new IWorkItemDummyService();
            IWorkItemDataContract dataContract = new WorkItemDataContractDummyObject();
            AutoResetEvent cpResetEvent = new AutoResetEvent(false);
            Dictionary<string, object> namedParameters = new Dictionary<string, object>();
            YakShaverWorkItem workItem = null;
            bool workflowCompleted = false;
            int notifyCount = 0;

            DataExchangeService.AddService(notificationService);
            DataExchangeService.AddService(analysisService);
            DataExchangeService.AddService(providerService);

            namedParameters.Add("WorkItemDataContract", dataContract);

            // created here because we are using delegates + anonymous methods instead of fully defined methods.
            WorkflowInstance workflowInstance = WorkflowEngine.CreateWorkflow(typeof(WorkItemEntryWorkflow), namedParameters);

            #region Handle Workflow Runtime Events

            WorkflowEngine.WorkflowPersisted += delegate(object sender, WorkflowEventArgs args)
            {
                if (args.WorkflowInstance.InstanceId == workflowInstance.InstanceId)
                {
                    SqlTrackingQuery sqlQuery = new SqlTrackingQuery(sqlTrackingConnectionString);
                    SqlTrackingWorkflowInstance sqlQueryInstance = null;
                    if (sqlQuery.TryGetWorkflow(args.WorkflowInstance.InstanceId, out sqlQueryInstance))
                    {
                        foreach (UserTrackingRecord trackingRecord in sqlQueryInstance.UserEvents)
                        {
                            if (trackingRecord.UserDataKey.Equals("NotifyCount"))
                                notifyCount = (int)trackingRecord.UserData;
                            if (trackingRecord.UserDataKey.Equals("WorkItem"))
                                workItem = (YakShaverWorkItem)trackingRecord.UserData;
                        }
                    }
                }
                return;
            };

            
            WorkflowEngine.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs args)
            {
                if (args.WorkflowInstance.InstanceId == workflowInstance.InstanceId)
                {
                    workflowCompleted = true;
                    cpResetEvent.Set();
                }
            };

            #endregion

            workflowInstance.Start();

            //  cpResetEvent.WaitOne(new System.TimeSpan(0, 0, 0, 45), true);

            Thread.Sleep(5000);
            //idled and in a timer we should be able to fire an event at it.

          
            workflowInstance = WorkflowEngine.GetWorkflow(workflowInstance.InstanceId);

            notificationService.RaiseWorkItemModifiedEvent(workflowInstance.InstanceId);
            cpResetEvent.WaitOne(new System.TimeSpan(0, 0, 0, 30), true);


            Assert.IsTrue(workflowCompleted);
            Assert.IsTrue(notifyCount > 0);
            Assert.IsTrue(notificationService.NotifyForInterventionMethodCallCount > 0);
            Assert.IsTrue(analysisService.AnalysisCallCount > 0);
            
        }
    }
}
